package com.youxin.chat.pay.service.channel.impl.ypl.util;


import com.youxin.base.BaseResultCode;
import com.youxin.chat.pay.utils.HttpClientConfig;
import com.youxin.exception.SystemException;
import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import javax.net.ssl.*;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.SocketException;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

public class YplHttpUtil {

    private static PoolingHttpClientConnectionManager phccm;

    public static String EMPTY_STR_RESULT = "";

    private static final Logger logger = LoggerFactory.getLogger(YplHttpUtil.class);
    /**
     * 初始化
     */
    private static void init() {
        if (phccm == null) {
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", trustAllHttpsCertificates())
                    .build();
            phccm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            phccm.setMaxTotal(HttpClientConfig.MAX_TOTAL);// 整个连接池最大连接数
            phccm.setDefaultMaxPerRoute(HttpClientConfig.MAX_PER_ROUTE);// 每路由最大连接数

            Runnable runnable = new Runnable() {
                @Override
                public void run() {

                    // 关闭过期的连接
                    phccm.closeExpiredConnections();

                    // 关闭空闲时间超过1秒的连接
                    phccm.closeIdleConnections(5, TimeUnit.MINUTES);
                    //logger.debug("cmd=HttpClientUtil:init, *********************清理无效的链接****************");
                }
            };
            ScheduledExecutorService service = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
                @Override
                public Thread newThread(Runnable r) {
                    return new Thread(r,"Ypl-http-scheduled");
                }
            });
            //Executors.newSingleThreadScheduledExecutor();
            // 第二个参数为首次执行的延时时间，第三个参数为定时执行的间隔时间
            service.scheduleAtFixedRate(runnable, 2, 30, TimeUnit.SECONDS);
        }
    }

    /**
     * 通过连接池获取HttpClient
     *
     * @return
     */
    private static CloseableHttpClient getHttpClient(boolean redirectsEnabled) {
        init();
        // 请求重试处理
        HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() {
            @Override
            public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
                if (executionCount >= HttpClientConfig.RETRY_NUM) {// 如果已经重试了8次，就放弃
                    return false;
                }
                if (exception instanceof NoHttpResponseException) {// 如果服务器丢掉了连接，那么就重试
                    return true;
                }
                if (exception instanceof SSLHandshakeException) {// 不要重试SSL握手异常
                    return false;
                }
                if (exception instanceof InterruptedIOException) {// 超时
                    return false;
                }
                if (exception instanceof UnknownHostException) {// 目标服务器不可达
                    return false;
                }
                if (exception instanceof ConnectTimeoutException) {// 连接被拒绝
                    return false;
                }
                if (exception instanceof SSLException) {// SSL握手异常
                    return false;
                }
                if (exception instanceof SocketException) {
                    return true;
                }
                HttpClientContext clientContext = HttpClientContext.adapt(context);
                HttpRequest request = clientContext.getRequest();
                // 如果请求是幂等的，就再次尝试
                if (!(request instanceof HttpEntityEnclosingRequest)) {
                    return true;
                }
                return false;
            }
        };

        // 创建全局的requestConfig
        RequestConfig requestConfig = RequestConfig.custom()
                //连接超时时间
                .setConnectTimeout(HttpClientConfig.CONNECT_TIMEOUT)
                .setSocketTimeout(HttpClientConfig.SOCKET_TIMEOUT)
                //从池中获取连接超时时间
                //建议设置500ms即可，不要设置太大，这样可以使连接池连接不够时不用等待太久去获取连接，不要让大量请求堆积在获取连接处，尽快抛出异常，发现问题。
                .setConnectionRequestTimeout(500)
                .setRedirectsEnabled(redirectsEnabled)
                .setCookieSpec(CookieSpecs.DEFAULT).build();
        // 声明重定向策略对象
        LaxRedirectStrategy redirectStrategy = new LaxRedirectStrategy();
        return HttpClients.custom().setConnectionManager(phccm).setDefaultRequestConfig(requestConfig)
                .setRedirectStrategy(redirectStrategy).setRetryHandler(httpRequestRetryHandler).build();
    }


    public static String getHttpPost(String url, Map<String,String> headers, String data, String pubKey) throws IOException {
        URL targetUrl = new URL(url);
        HttpClient httpclient = getHttpClient(false);
        headers.put("Content-Type", "application/json;charset=utf-8");

        HttpPost httpPost = new HttpPost(targetUrl.getPath());
        HttpHost httpHost = new HttpHost(targetUrl.getHost(), targetUrl.getPort(), targetUrl.getProtocol());
        Iterator iterator = headers.entrySet().iterator();

        while(iterator.hasNext()) {
            Map.Entry<String, String> entry = (Map.Entry)iterator.next();
            httpPost.setHeader((String)entry.getKey(), (String)entry.getValue());
        }
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(20*1000).setConnectTimeout(20*1000).build();
        httpPost.setConfig(requestConfig);
        StringEntity entity = new StringEntity(data, "utf-8");
        httpPost.setEntity(entity);
        String resp = (String) httpclient.execute(httpHost, httpPost, new ResponseHandler<String>() {
            @Override
            public String handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
                int status = response.getStatusLine().getStatusCode();
                if(status != 200){
                    return "";
                }
                String result = EntityUtils.toString(response.getEntity(), "utf-8");
                logger.info("data={}",result);
                if(response.getFirstHeader("x-efps-sign") == null){
                    throw new SystemException(BaseResultCode.COMMON_FAIL,"获取签名失败");
                }
                String sign = response.getFirstHeader("x-efps-sign").getValue();

                if(StringUtils.isEmpty(sign) || StringUtils.isEmpty(result) || !YplSignUtil.verify(result,pubKey,sign) ){
                    throw new SystemException(BaseResultCode.COMMON_FAIL,"易票联签名验证失败");

                }
                return result;
            }
        });
        return resp;
    }

    /**
     * 绕过验证
     *
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    public static SSLConnectionSocketFactory trustAllHttpsCertificates() {
        SSLConnectionSocketFactory socketFactory = null;
        TrustManager[] trustAllCerts = new TrustManager[1];

        // 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
        X509TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(
                    java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                    String paramString) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(
                    java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
                    String paramString) throws CertificateException {
            }

            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        };
        trustAllCerts[0] = trustManager;
        SSLContext sc = null;

//        ctx.init(null, new TrustManager[]{tm}, null);
//        SSLSocketFactory ssf = new SSLSocketFactory(ctx,SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
//        ClientConnectionManager ccm = this.getConnectionManager();
//        SchemeRegistry sr = ccm.getSchemeRegistry();


        try {
            sc = SSLContext.getInstance("TLS");//sc = SSLContext.getInstance("TLS")
            sc.init(null, trustAllCerts, null);
            socketFactory = new SSLConnectionSocketFactory(sc, NoopHostnameVerifier.INSTANCE);
        } catch (Exception e) {
            logger.error("创建跳过证书ssl异常", e);
            throw new SystemException(BaseResultCode.COMMON_FAIL, "创建跳过证书ssl异常");
        }
        return socketFactory;
    }
}
